home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
Book Chapters
/
00 - Game Programming Primer
/
HelloWorld2.c
< prev
next >
Wrap
Text File
|
1995-06-26
|
14KB
|
326 lines
//==============================================================================================\\
// ----------------------------------------------------------------------------------- \\
// HelloWorld2.c version 1.0.0 copyright © 1993…1995 Jamie McCornack, john calhoun \\
// ----------------------------------------------------------------------------------- \\
// Demo program for Macintosh GameWriter 1.0.0, a training program… \\
// …for beginning Mac game programmers. MGW1 includes MGWExterns1.h, MGWUtilities1.c,… \\
// …MGWSound1.c, MGWGraphics1.c, MGWGraphicsBWLite1.c, HelloWorld.rsrc and an assortment… \\
// …of demo programs; projects HelloWorld1.π etc. and source code files HelloWorld1.c etc. \\
// A tutorial is available in Tricks of the Mac Game Programming Gurus, published… \\
// …by Hayden Books, August 1995. \\
// \\
// This code is offered by the copyright holders for no fee and for whatever use… \\
// …you care to make of it, but we do hope you remember where it came from. \\
// \\
// Please send bug reports to MacGameDev at America OnLine. macgamedev@aol.com \\
// Suggestions and observations are also appreciated. \\
// Updates and upgrades will be available now and then from the above e-mail address. \\
//==============================================================================================\\
// This program opens a window, displays an 8-bit color background, and at first…
// …mouseclick, runs a clam across the screen. At second mouseclick, the clam stops,…
// …talks, and flaps its/his/her face for 40 frames. Then the program quits.
#include "MGWExterns1.h"
#define kPutInFront (WindowPtr)-1L
#define kWaitTicks 4L // Sets the delay in Ticks between frames. Try 3. Try 2. Try 0.
#define kStepLength 14 // Sets the distance in pixels between sprite moves.
#define kFrontFace 0
#define kBlinkFace 1
#define kEehFace 2
#define kOohFace 3
#define kStepRightFace 4
#define kWalkRightFace 5
#define kRunRightFace 6
#define kMaxFaces 7
// The resource constants--with 'r' prefix like Apple wants them.}
#define rFacesID 136 // The 'PICT' ID# where the views of the clam are located.}
#define rMasksID 130 // The 'PICT' ID# where the clam masks are located.}
#define rBackgroundID 134 // The 'PICT' ID# where the background picture is located.}
#define rMainWindowID 128 // The 'WIND' resource ID# for the main window.}
#define rHelloSndID 3000
#define rFootstepSndID 3001
#define rImpactSndID 3002
#define rDizzySndID 3003
#define kColorBitsNeeded 8
typedef struct
{
Rect face;
Rect mask;
} tSpriteType;
Rect bigPictureRect, facesRect, masksRect, clamIsAtRect, clamWasAtRect, clamComboRect;
CGrafPtr workCPort, facesCPort, backgroundCPort;
GrafPtr mainWindow, masksPort;
Boolean itWorked;
long targetTick;
short thisSprite, thisFaceCounter;
tSpriteType sprite[kMaxFaces];
extern Boolean gUserWantsSound;
//============================================================== Prototypes
void InitAll(void);
void OpenMainWindow (void);
void SetTheRects(void);
void SetTheCPorts(void);
void ShowClam (void);
void DoDelay (void);
void RunRight (void);
void LipSynch (void);
//============================================================== Functions
//-------------------------------------------------------------- InitAll
void InitAll(void)
{
InitToolbox();
if (WhatsOurDepth() != kColorBitsNeeded) // Compare color depth with what…
RedAlert(kErrNot8BitColor); // …we want. If not equal, exit.
gUserWantsSound = TRUE;
InitializeForSound();
SetTheRects(); // Since some of these Rects define fields in CGrafPorts,…
SetTheCPorts(); // …set the rects before setting the ports.
thisSprite = kFrontFace;
thisFaceCounter = 0;
targetTick = TickCount() + kWaitTicks;
HideCursor(); // This demo doesn't use mouse input, so why look at it?
}
//-------------------------------------------------------------- OpenMainWindow
void OpenMainWindow (void)
{
mainWindow = GetNewCWindow(128, 0L, kPutInFront); // Load window from resource.
ShowWindow((GrafPtr)mainWindow); // Now display it.
SetPort((GrafPtr)mainWindow); // Make its port current.
ClipRect(&bigPictureRect); // Set its clip region.
CopyRgn(mainWindow->clipRgn, mainWindow->visRgn); // Set its visRgn.
ForeColor(blackColor); // Set its pen color to black.
BackColor(whiteColor); // Set background color white.
}
//-------------------------------------------------------------- SetTheRects
void SetTheRects(void) // The most tedious part of programming this type of game.
{
SetRect(&facesRect, 0, 0, 448, 64); // Size and shape of BitMap for the sprite faces.
SetRect(&masksRect, 0, 0, 448, 32); // Size and shape of BitMap for the sprite masks.
SetRect(&bigPictureRect, 0, 0, 512, 322); // The shape of the picture we'll put in the main window, workCPort and backgroundCPort.}
SetRect(&clamIsAtRect, 200, 244, 232, 276); // The shape (32 x 32) of the images of Clem, and the position of the first image.}
clamWasAtRect = clamIsAtRect; // Initializing clamIsAtRect...it has to start somewhere, and this is handy.}
clamComboRect = clamIsAtRect;
// And now, the tedium. In this sample, all we're doing is showing the clam running across the screen to the right.}
// However, if you use ResEdit and look at 'PICT' 129 in Sample.rsrc, you'll find 28 different views of the clam.}
// If we wanted the clam to run left too, and walk slowly, and face the user, and blink its eyes, we'd be calling…}
// …SetRect 56 times--one face and one mask per sprite. And if we had jumping clams and rear views of clams…}
// …and starfish and clamdiggers and other hazards of the clam environment, we might have hundreds of rects to set.}
SetRect(&sprite[kFrontFace].face, 320, 32, 352, 64); // The shape and position of sprite[kFrontFace].face on facesCPort.portPixMap.
SetRect(&sprite[kFrontFace].mask, 320, 0, 352, 32); // The shape and position of sprite[kFrontFace].mask on masksPort.portPixMap.
SetRect(&sprite[kBlinkFace].face, 320, 0, 352, 32); // Note that some faces (e.g. eyes open or closed) use the same mask,…
SetRect(&sprite[kBlinkFace].mask, 320, 0, 352, 32); // …since they have the same silhouette.}
SetRect(&sprite[kEehFace].face, 352, 0, 384, 32); // I could write more comments here, but setting these rects…
SetRect(&sprite[kEehFace].mask, 352, 0, 384, 32); // …is already tedious enough without a bunch of busy-work.
SetRect(&sprite[kOohFace].face, 352, 32, 384, 64);
SetRect(&sprite[kOohFace].mask, 352, 0, 384, 32);
SetRect(&sprite[kStepRightFace].face, 192, 0, 224, 32);
SetRect(&sprite[kStepRightFace].mask, 192, 0, 224, 32);
SetRect(&sprite[kWalkRightFace].face, 224, 0, 256, 32);
SetRect(&sprite[kWalkRightFace].mask, 224, 0, 256, 32);
SetRect(&sprite[kRunRightFace].face, 160, 0, 192, 32); // BTW, there are plenty more clam faces and masks in the 'PICT's,…
SetRect(&sprite[kRunRightFace].mask, 160, 0, 192, 32); // …if you feel you need rect setting practice. :-)
}
//-------------------------------------------------------------- SetTheCPorts
void SetTheCPorts(void) // Create the CGrafPorts and load their .portPixMap fields.
{
// Create BitMap for sprite masks. NOTE THIS IS A BITMAP!
CreateOffScreenBitMap (&masksRect, &masksPort);
LoadGraphic (rMasksID); // …load 'PICT' resource for the clam masks.
// Create PixMap for sprite faces.
CreateOffScreenPixMap (&facesRect, &facesCPort);
LoadGraphic (rFacesID); // …load 'PICT' resource for the clam faces.
// Create PixMap for the background.
CreateOffScreenPixMap (&bigPictureRect, &backgroundCPort);
LoadGraphic(rBackgroundID); // …load 'PICT' resource for the background picture.
// Create PixMap for offscreen graphics work.
CreateOffScreenPixMap (&bigPictureRect, &workCPort);
OpenMainWindow();
//{This fills the main window with the background picture, so the user can see it.
CopyBits(&((GrafPtr)backgroundCPort)->portBits,
&((GrafPtr)mainWindow)->portBits,
&bigPictureRect, &bigPictureRect, srcCopy, mainWindow->visRgn);
// This fills the workCPort.portPixMap with the background picture, so updates can be done quickly.
CopyBits(&((GrafPtr)backgroundCPort)->portBits,
&((GrafPtr)workCPort)->portBits,
&bigPictureRect, &bigPictureRect, srcCopy, mainWindow->visRgn);
}
//-------------------------------------------------------------- ShowClam
void ShowClam (void) // Do the animation and make it appear on the screen.
{
CopyMask(&((GrafPtr)facesCPort)->portBits,
&((GrafPtr)masksPort)->portBits,
&((GrafPtr)workCPort)->portBits,
&sprite[thisSprite].face,
&sprite[thisSprite].mask,
&clamIsAtRect);
// Now there is an image of a clam in the new position in workMap. If we had done this work in…
// mainWindow, we would have seen considerable flickering. Instead, we did it offscreen, and left the…
// previous image of the clam visible on the screen while we worked.
UnionRect(&clamWasAtRect, &clamIsAtRect, &clamComboRect);
// Find the smallest rectangle which will cover the old position of the clam and the new.
CopyBits(&((GrafPtr)workCPort)->portBits,
&(((GrafPtr)mainWindow)->portBits),
&clamComboRect, &clamComboRect, srcCopy, mainWindow->visRgn);
// Copy the contents of comboRect from workCPort->portPixMap to the main window. In one swell foop, the old clam…}
//…will be erased, and the new clam overlayed onto the background picture. Wallah! Flicker-free animation!}
CopyBits(&((GrafPtr)backgroundCPort)->portBits,
&(((GrafPtr)workCPort)->portBits),
&clamIsAtRect, &clamIsAtRect, srcCopy, mainWindow->visRgn);
// Restore the workCPort by covering up our clam with the background it obscures.
// This way, workCPort->portPixMap is identical to backgroundCPort->portPixMap,…
// without having to copy the entire PixMap.
}
//-------------------------------------------------------------- DoDelay
// This is the companion function to the above function (LogNextTick()).
// We do nothing but loop until TickCount() catches up with (or passes) our…
// global variable tickNext.
void DoDelay (void)
{
do
{
}
while (TickCount() < targetTick); // Loop until TickCount() catches up.
targetTick = TickCount() + kWaitTicks;
}
//-------------------------------------------------------------- RunRight
void RunRight (void) //Gives a sequence of views of the clam running to the right.
{
switch (thisSprite) //If the current view of the clam is…
{ case (kStepRightFace): // …kStepRightFace, then set thisSprite to…
{ thisSprite = kWalkRightFace; // …kWalkRightFace, and if it is currently…
break; }
case (kWalkRightFace): // …kWalkFace, then set it to…
{ thisSprite = kRunRightFace; // …kRunRightFace.
PlayASound(rFootstepSndID, kHighestSoundPriority); // Note sound priority.
break; }
// And if it was neither kStepRightFace nor kWalkRightFace, then thisSprite was either…
default : // …kRunRightFace, or what it was when RunRight()…
thisSprite = kStepRightFace; // …was called, so we set it to kStepRightFace
}
clamWasAtRect = clamIsAtRect; // Store the clam's current position as its last position,…
// …we'll be erasing it next time through the loop.
OffsetRect(&clamIsAtRect, kStepLength, 0); // Set the clam's next position--it'll be…
// …kStepLength pixels to the right of its last position.
if (clamIsAtRect.left > 512) // If the clam has wandered out of sight,…
{ // …set the right border of clamIsAtRect…
clamIsAtRect.right = 0; // …to the left edge of the screen…
clamIsAtRect.left = -32; // …and move the left border of clamIsAtRect…
} // …as needed to maintain its 32 x 32 shape & size.
ShowClam(); // The actual animation routine!
DoDelay(); // Do nothing for a while. In a real game, you'll want to use time more wisely.
}
//-------------------------------------------------------------- LipSynch
// This routine has the clam stop moving, face the screen, and move its face.
void LipSynch (void)
{
while (thisFaceCounter < 40)
{
thisFaceCounter = thisFaceCounter + 1;
switch (thisFaceCounter)
{ case (1):
{ thisSprite = kFrontFace;
break; }
case (2):
{ thisSprite = kEehFace;
break; }
case (4):
{ thisSprite = kFrontFace;
break; }
case (5):
{ thisSprite = kOohFace;
break; }
case (9):
{ thisSprite = kEehFace;
break; }
case (11):
{ thisSprite = kOohFace;
break; }
case (13):
{ thisSprite = kFrontFace;
break; }
case (16):
{ thisSprite = kBlinkFace;
break; }
case (18):
{ thisSprite = kFrontFace;
break; }
case (35):
{ thisSprite = kBlinkFace;
break; }
case (38):
{ thisSprite = kFrontFace;
break; }
}
ShowClam();
DoDelay(); // Do nothing for a while, so the lips don't flap too fast.
}
}
//-------------------------------------------------------------- main
//----------------------------------------------------------------------
void main(void)
{
InitAll();
while (!Button()) // Before the user presses the mouse button, nothing happens.
{
}
while (Button()) // When the user presses the mouse button, nothing happens.
{
}
while (!Button()) // When the mouse button is released, continue to…
{
RunRight(); // …show the clam running right (duh),…
}
while (Button()) // …until the button is pushed.
{ // Do nothing until the button is released.
}
PlayASound(rHelloSndID, kMediumSoundPriority);
LipSynch();
CloseDownSound();
} // And we're done.
//------------------------------------------------------------------------------------------\\
// End HelloWorld1.c \\
//------------------------------------------------------------------------------------------\\